import java.io.InputStream;
import java.util.Vector;

public class symbol_table
{
	//global variables
	public static int show=0;
	/*
	 * meaning of show:(in funtion tableList)
	 * 0:bug information
	 * 1:table list
	 * 2:funtion table list
	 */
	public static boolean success=true;
	public static int index=0;//to memorize the index of table
	public static int findex=0;//to memorize the index of funtion table
	public static int vindex=0;//to memorize the index of allVariables
	
	public static Vector[] table=new Vector[100];//default size is 100
	/*
	 * the index of table[x] means:
	 * 0:id
	 * 1:name
	 * 2:type
	 * 3:line
	 * 4:column
	 * 5:xc//is a xing can
	 * 6:isArray
	 */
	
	public static Vector[] F_table=new Vector[100];
	/*
	 * the index of F_table[x] means:
	 * 0:id
	 * 1:name
	 * 2:return_type
	 * 3:line
	 * 4:column
	 * 5:Identifier
	 * 6:type
	 * 7:parameter
	 * 8,9,10...:the same as 5,6,7
	 */
	
	public static Vector[] allVariables=new Vector[1024];
	/*
	 * the index of allVariables[x] means:
	 * 0:name
	 * 1:line
	 * 2:column
	 */
	
	//initialize the symbol_table
	public  symbol_table(InputStream is)
	{
		index=0;
		vindex=0;
		findex=0;
		//initialize the tables
		for(int i=0;i<100;i++)
			table[i]=new Vector();
		for(int i=0;i<1024;i++)
			allVariables[i]=new Vector();
		for(int i=0;i<100;i++)
			F_table[i]=new Vector();
		/*add the class out and its imformation to table&F_table
		class out{
			void F_writeInt P_int:(int)integerToWrite{}
			void F_writeChar P_char:(int)charToWrite{}
			}
		*/
		  //class out
		  table[index].add(index+1);//index+1 is the id
		  table[index].add("out");
		  table[index].add("class");
		  table[index].add(0);
		  table[index].add(0);
		  table[index].add(false);
		  table[index].add(false);
		  index++;
		  //integerToWrite
		  table[index].add(index+1);//index+1 is the id
		  table[index].add("integerToWrite");
		  table[index].add("int");
		  table[index].add(0);
		  table[index].add(0);
		  table[index].add(true);
		  table[index].add(false);
		  index++;
		  //charToWrite
		  table[index].add(index+1);//index+1 is the id
		  table[index].add("charToWrite");
		  table[index].add("char");
		  table[index].add(0);
		  table[index].add(0);
		  table[index].add(true);
		  table[index].add(false);
		  index++;
		  //F_writeInt
		  F_table[findex].add(findex+1);
		  F_table[findex].add("F_writeInt");
		  F_table[findex].add("void");
		  F_table[findex].add(0);
		  F_table[findex].add(0);
		  F_table[findex].add("P_int");
		  F_table[findex].add("int");
		  F_table[findex].add("integerToWrite");
		  findex++;
		  //F_writeChar 
		  F_table[findex].add(findex+1);
		  F_table[findex].add("F_writeChar");
		  F_table[findex].add("void");
		  F_table[findex].add(0);
		  F_table[findex].add(0);
		  F_table[findex].add("P_char");
		  F_table[findex].add("char");
		  F_table[findex].add("charToWrite");
		  findex++;
		  /*end of the origin table&F_table*/
		FishParserTokenManager tokenManager=new FishParserTokenManager(new JavaCharStream(is));
	  try
	  {
	  Token t=tokenManager.getNextToken();
	  Token Type=null;
	  Token Variable=null;//to memorize the possible variable
	  int arrayIndex=0;
	  boolean xc=false;//whether it is a xing can
	  boolean arraySize=false;
	  boolean define=false;//to memorize if the variable is defined
	  boolean isArray=false;//to judge if the variable is an array
	  boolean isVariable=false;//to judge if the token is a variable
	  while (t.image.length()>0) 
	  {
		  //search for the variables defination
		  if(t.image.equals("int")||t.image.equals("float")
				  ||t.image.equals("class")||t.image.equals("boolean")
				  ||t.image.equals("char")||t.image.equals("void"))
		  {
			  define=true;
			  Type=t;//the type is always before the first variable
		  }
		  else if(define==true&&t.image.equals("["))
		  {
			  isArray=true;
		  }
		  else if(define==true&&t.image.equals(")"))
		  {
			  xc=true;
		  }
		//check if it is a variable
		  else if(define==true&&t.image.substring(0,1).matches("[a-zA-Z]+")
				  &&!t.image.substring(0,1).equals("F")
				  &&!t.image.substring(0,1).equals("P"))
		  {
			  table[index].add(index+1);//index+1 is the id
			  table[index].add(t.image);
			  table[index].add(Type.image);
			  table[index].add(t.beginLine);
			  table[index].add(t.beginColumn);
			  table[index].add(xc);
			  table[index].add(isArray);
			  if(findex>0&&t.beginLine==
					  Integer.parseInt(F_table[findex-1].get(3).toString()))
			  {
				  F_table[findex-1].add(Type.image);
				  F_table[findex-1].add(t.image);
			  }
			  if(isArray)
				  arrayIndex=index;
			  define=false;
			  isArray=false;
			  xc=false;
			  index++;
		  }
		  //check if it is a funtion
		  else if(define==true&&t.image.substring(0,1).equals("F"))
		  {
			  String isArr="";
			  if(isArray)
				  isArr+="[]";
			  F_table[findex].add(findex+1);
			  F_table[findex].add(t.image);
			  F_table[findex].add(Type.image+isArr);
			  F_table[findex].add(t.beginLine);
			  F_table[findex].add(t.beginColumn);
			  define=false;
			  isArray=false;
			  findex++;
		  }
		  else if(findex>0&&t.image.length()>1&&t.image.substring(0,1).equals("P"))
		  {
			  if(t.beginLine==Integer.parseInt(F_table[findex-1].get(3).toString()))
					  F_table[findex-1].add(t.image);
		  }
		  //end of sentence
		  if(t.image.equals(";"))
		  {
			  isArray=false;
			  define=false;
			  arraySize=false;
			  xc=false;
		  }
		  //give arraySize
		  if(t.image.equals("new"))
		  {
			  arraySize=true;
		  }
		  if(arraySize&&t.image.charAt(0)>='0'&&t.image.charAt(0)<='9')
		  {
			  table[arrayIndex].removeElementAt(6);
			  table[arrayIndex].add(t.image);
			  arraySize=false;
		  }
		  //memorize all the variables
		  if((t.image.substring(0,1).matches("[a-zA-Z]+"))&&
				  !(t.image.equals("int")||t.image.equals("float")
				  ||t.image.equals("class")||t.image.equals("boolean")
				  ||t.image.equals("char")||t.image.equals("void")
				  ||t.image.equals("if")||t.image.equals("for")
				  ||t.image.equals("return")||t.image.equals("this")
				  ||t.image.equals("true")||t.image.equals("false")
				  ||t.image.equals("new")
				  ||t.image.substring(0,1).equals("F")
				  ||t.image.substring(0,1).equals("P")))
		  {
			  isVariable=true;
			  Variable=t;
		  }
		  if(isVariable==true&&t.image!=":")
		  {
			  allVariables[vindex].add(Variable.image);
			  allVariables[vindex].add(Variable.beginLine);
			  allVariables[vindex].add(Variable.beginColumn);
			  vindex++;
			  isVariable=false;
		  }
		  else if(isVariable==true&&t.image==":")
		  {
			  isVariable=false;
		  }
		  t=tokenManager.getNextToken();
	   }
	  }
	  catch(java.lang.ArrayIndexOutOfBoundsException e)
	  {
		  success=false;
	  }catch(TokenMgrError ee)
	  {
		  success=false;
	  }
	 }
	public String tableList()
	{
		String str="";
		if(show==0)
			str+=check();
		else if(show==1)//means there is no mistake
		{
			str+="id\tname\t(return)type\tline\tcolumn\txc\tisArray\n";
			for(int i=0;i<index;i++)
			{
				for(int j=0;j<table[i].size();j++)
					str+=table[i].get(j).toString()+"\t";
				str+="\n";
			}
		}
		else if(show==2)
		{
			str+="id\tname\treturn_type\tline\tcolumn\tList\n";
			for(int i=0;i<findex;i++)
			{
				for(int j=0;j<F_table[i].size();j++)
					str+=F_table[i].get(j).toString()+"\t";
				str+="\n";
			}
		}
		return str;
	}
	public void addTable(String name,String type)
	{
		//debug
		System.out.print("Insert into symbol table: " + name + " " + type + "\n");
		
		  table[index].add(index+1);//index+1 is the id
		  table[index].add(name);
		  table[index].add(type);
		  table[index].add(0);
		  table[index].add(0);
		  table[index].add(false);
		  table[index].add(false);
		  index++;
	}
	public int getFindex()
	{
		return findex;
	}
	public String[] getFuntionNames()
	{
		String[] funtionNames=new String[findex];
		for(int i=0;i<findex;i++)
		{
			funtionNames[i]=F_table[i].get(1).toString();
		}
		return funtionNames;
	}
	public String check()
	{
		String wrong="";//to memorize the information of the mistake
		
		//1,check if the variable has been defined twice
		for(int i=0;i<index;i++)
			for(int j=0;j<i;j++)
				if(table[i].get(1).toString().equals(table[j].get(1).toString()))
					wrong+="The variable \""+table[i].get(1).toString()
					+"\" has been defined more than once at line:"+table[i].get(3).toString()
					+" column:"+table[i].get(4).toString()+"!\n";
		
		//2,check if the variable is used without defined
	   if(wrong.length()==0)
	   {
		for(int i=0;i<vindex;i++)
		{
			boolean inTable=false;
			for(int j=0;j<index;j++)
			{
				if(allVariables[i].get(0).toString().equals(table[j].get(1).toString()))
				{
					inTable=true;
					if(Integer.parseInt(allVariables[i].get(1).toString())
							<Integer.parseInt(table[j].get(3).toString())
							||(allVariables[i].get(1).toString().equals(table[j].get(3).toString())
									&&Integer.parseInt(allVariables[i].get(2).toString())
									<Integer.parseInt(table[j].get(4).toString()) ))
						wrong+="The variable \""+allVariables[i].get(0).toString()
						+"\" is used without defined at line:"
						+allVariables[i].get(1).toString()
						+",colume:"+allVariables[i].get(2).toString()+"!\n";
				}
			}
			//not in table:
			if(!inTable)
				wrong+="The variable \""+allVariables[i].get(0).toString()
				+"\" is used without defined at line:"
				+allVariables[i].get(1).toString()
				+",colume:"+allVariables[i].get(2).toString()+"!\n";
		}
	   }
		if(wrong.length()==0)
			wrong+="Congratulations!There is no symbol table mistake!";
		return wrong;
	}
}
